using Unity.Entities;
using Unity.Physics;
using UnityEngine;

namespace Gj
{
    /// <summary>
    ///     引擎通知接口
    /// </summary>
    /// <summary>
    ///     技能施放基础引擎
    /// </summary>
    public class BaseEngine : MonoBehaviour
    {
        public string key;
        public Skill skill;
        public int time;
        private Notice _notice;
        [SerializeField]
        protected Vector3[] _positions;

        protected Cast _self;
        private float _startTime;
        private bool _sustaining;
        protected LifeChange[] _targetChanges;
        
        [SerializeField]
        protected Life[] _targets;
        private float _time;
        private bool _waiting;
        protected Entity entity;

        protected bool multiple = false;

        protected float power;
        protected bool sign = false;

        protected Life _target
        {
            get
            {
                if (_targets != null && _targets.Length > 0)
                    return _targets[0];
                if (_targetChanges != null && _targetChanges.Length > 0)
                    return _targetChanges[0].target;
                return null;
            }
        }

        protected Vector3 _position
        {
            get
            {
                if (_positions == null || _positions.Length == 0) return Vector3.zero;
                return _positions[0];
            }
        }

        protected virtual void OnDestroy()
        {
        }

        /// <summary>
        ///     初始化 设置技能和施放对象
        /// </summary>
        /// <param name="t"></param>
        /// <param name="s"></param>
        public void Init(Cast c, Skill s, int t)
        {
            _self = c;
            skill = s;
            time = t;
            Init();
        }

        public void Init(Cast c, Skill s)
        {
            _self = c;
            skill = s;
            Init();
        }

        public void Init(Cast c)
        {
            _self = c;
            Init();
        }

        private void Init()
        {
            power = 0;
            _targets = null;
            _positions = null;
            _startTime = 0;
            _time = 0;
            InitEntity();
            OnInit();
        }

        private void InitEntity()
        {
            if (entity == Entity.Null) entity = ObjectService.EntityManager.CreateEntity();
            ObjectService.EntityManager.AddComponentData(entity,
                new SkillData
                {
                    skillId = skill.refInfo.id, power = 0, time = time, master = _self.GetEntity(),
                    targetRelation = skill.targetRelation
                });
        }

        protected virtual void OnInit()
        {
        }

        protected GameObject GetOrigin()
        {
            if (_self == null) return null;
            return _self.GetCastObject().GetOrigin(skill);
        }

        protected GameObject GetOrigin(int index)
        {
            if (_self == null) return null;
            return _self.GetCastObject(index).GetOrigin(skill);
        }

        protected GameObject GetHit()
        {
            if (_target == null) return null;
            return _target.GetHit(_self);
        }

        protected GameObject GetHit(int index)
        {
            if (_self == null) return null;
            return _target.GetHit(index, _self);
        }

        protected int GetNumber()
        {
            return _self.GetNumber();
        }

        protected int GetTotal()
        {
            return _self.GetTotal();
        }

        string GetKey()
        {
            return key;
        }

        protected GameObject GetEffect(GameObject obj = null)
        {
            return ObjectService.single.MakeEffect(StringTools.IsEmpty(GetKey()) ? string.Format("{0}/{1}", "Skill", skill.effect) : string.Format("{0}/{1}/{2}", "Skill", skill.effect, GetKey()),
                obj != null ? obj : gameObject);
        }

        protected virtual void OnInitEffect()
        {
        }

        protected virtual void OnDestroyEffect()
        {
        }

        public void UpdateData()
        {
            OnUpdateData();
        }

        protected virtual void OnUpdateData()
        {
        }

        /// <summary>
        ///     设置目标
        /// </summary>
        /// <param name="target"></param>
        public BaseEngine Set(Life target)
        {
            Set(new[] { target });
            return this;
        }

        public BaseEngine Set(Life[] targets)
        {
            Debug.LogError(targets.Length);
            _targets = targets;
            return this;
        }

        public bool HasPosition()
        {
            return _positions != null;
        }

        public bool HasTarget()
        {
            return (_targets != null && _targets.Length != 0) || (_targetChanges != null && _targetChanges.Length != 0);
        }

        public Life GetTarget()
        {
            return _target;
        }

        /// <summary>
        ///     设置目标点
        /// </summary>
        /// <param name="position"></param>
        public BaseEngine Set(Vector3 position)
        {
            Set(new[] { position });
            return this;
        }

        public BaseEngine Set(Vector3[] positions)
        {
            _positions = positions;
            return this;
        }

        public BaseEngine Set(float t)
        {
            _time = t;
            return this;
        }

        public Vector3 GetPosition()
        {
            return _position;
        }

        public BaseEngine Set(LifeChange targetChange)
        {
            Set(new[] { targetChange });
            return this;
        }

        public BaseEngine Set(LifeChange[] targetChanges)
        {
            _targetChanges = targetChanges;
            return this;
        }

        protected void UpdatePower()
        {
            if (_startTime == 0)
            {
                power = 0;
                return;
            }

            power = (Time.time - _startTime + _time) / skill.readyTime;
            if (power < 0.15f) power = 0.15f;
            if (power > 1) power = 1;
        }

        /// <summary>
        ///     开火 设置通知
        /// </summary>
        /// <param name="engineNotice"></param>
        public void Ignition(Notice engineNotice)
        {
            _notice = engineNotice;
            Ignition();
        }

        public void Ignition()
        {
            Log("Ignition");
            OnInitEffect();
            OnIgnition();
            if (skill.castType == SkillCastType.Now || skill.castType == SkillCastType.Sustained)
                ReadyCast();
            else
                Ready();
        }

        protected virtual void OnIgnition()
        {
        }

        /// <summary>
        ///     准备
        /// </summary>
        private void Ready()
        {
            _startTime = Time.time;
            _notice?.Ready(skill);
            _waiting = true;
            if (_self.IsAuto()) Invoke("ReadyEnd", skill.readyTime);
        }


        /// <summary>
        ///     准备结束
        /// </summary>
        public void ReadyEnd()
        {
            _waiting = false;
            ReadyCast();
        }

        /// <summary>
        ///     确认施放
        /// </summary>
        public void Confirm()
        {
            if (_waiting)
            {
                CancelInvoke("ReadyEnd");
                ReadyEnd();
            }
        }

        private void ReadyCast()
        {
            Log("ReadyCast");
            if (sign)
            {
                if (!CheckTargets())
                {
                    Cancel();
                    return;
                }

                SignTargets();
            }

            _notice?.Start(skill);

            var castTime = skill.castTime;
            if (castTime > 0)
                Invoke("Now", castTime);
            else
                Now();
        }

        /// <summary>
        ///     触发
        /// </summary>
        public void Now()
        {
            Log("Now");
            if (skill.castType == SkillCastType.Sustained || skill.castType == SkillCastType.ReadyAndSustained)
            {
                _sustaining = true;
                StartCast();
                if (skill.intervalTime > 0) InvokeRepeating("SustainedCast", 0, skill.intervalTime);
                if (skill.sustainedTime > 0)
                {
                    _self.OnConsume(skill, time);
                    Cast();
                    Invoke("End", skill.sustainedTime);
                }
            }
            else if (_self.OnInspect(skill, time))
            {
                _self.OnConsume(skill, time);
                if (multiple)
                {
                    if (skill.intervalTime > 0)
                        InvokeRepeating("Cast", 0, skill.intervalTime);
                    else if (skill.number > 0)
                        for (var i = 0; i < skill.number; i++)
                            Cast();
                }
                else
                {
                    Cast();
                }

                if (skill.sustainedTime > 0) Invoke("End", skill.sustainedTime);
            }
            else
            {
                Cancel();
            }
        }

        protected void Over()
        {
            CancelInvoke("Cast");
        }

        protected void Stop()
        {
            Log("Stop");
            End();
        }

        /// <summary>
        ///     结束
        /// </summary>
        private void End()
        {
            if (_sustaining)
            {
                CancelInvoke("SustainedCast");
                EndCast();
                _sustaining = false;
            }

            _notice?.End(skill);
            Destroy();
        }

        /// <summary>
        ///     取消
        /// </summary>
        public void Cancel()
        {
            if (_waiting)
            {
                if (_self.IsAuto()) CancelInvoke("ReadyEnd");
                _waiting = false;
                _notice?.Stop(skill);
            }
            else if (_sustaining)
            {
                EndCast();
                CancelInvoke("SustainedCast");
                CancelInvoke("End");
                _sustaining = false;
                _notice?.Cancel(skill);
            }
            else
            {
                if (sign) HitTarget();
            }

            Destroy();
        }

        /// <summary>
        ///     具体执行函数
        /// </summary>
        protected bool CheckTargets()
        {
            if (_targetChanges != null)
            {
                Log("CheckTargets", "{0}", _targetChanges.Length);
                for (var i = 0; i < _targetChanges.Length; i++)
                    if (_targetChanges[i].target == null)
                        return false;
            }

            return true;
        }

        protected virtual void Cast()
        {
        }

        protected virtual void StartCast()
        {
        }

        protected virtual void EndCast()
        {
        }

        /// <summary>
        ///     具体执行函数
        /// </summary>
        protected virtual void SustainedCast()
        {
            if (skill.sustainedTime > 0)
            {
                Cast();
                return;
            }

            if (_self.OnInspect(skill, time))
            {
                _self.OnConsume(skill, time);
                Cast();
            }
            else
            {
                Cancel();
            }
        }

        /// <summary>
        ///     对碰撞点触发
        /// </summary>
        /// <param name="position"></param>
        protected void CastTarget(Vector3 position)
        {
            if (skill.range <= 0) return;
            ObjectService.EntityManager.AddComponentData(entity, new CastColliderData
            {
                type = ColliderType.Sphere,
                Start = position,
                End = position,
                radius = skill.range * (1 + power)
            });
        }

        protected void CastTarget(Vector3 start, Vector3 end)
        {
            ObjectService.EntityManager.AddComponentData(entity, new CastRayData
            {
                Start = start,
                End = end,
                multiple = true
            });
        }

        protected void CastTarget(Entity[] targets)
        {
            if (targets == null || targets.Length == 0) return;
            var buffer = ObjectService.EntityManager.AddBuffer<CastTargetsData>(entity);
            for (var i = 0; i < targets.Length; i++) buffer.Add(new CastTargetsData { entity = targets[i] });
        }

        protected void SignTarget()
        {
            SignTargets();
        }

        protected void SignTargets()
        {
            if (_targetChanges != null)
            {
                Log("SignTargets", "{0}", _targetChanges.Length);
                for (var i = 0; i < _targetChanges.Length; i++) _targetChanges[i].target.SignChange(_self, skill, 1);
            }
        }

        protected void HitTarget()
        {
            HitTargets();
        }

        protected void HitMultipleTarget(int index, bool hit = true)
        {
            // if (index == 0) _self.OnHit(skill, time);
            if (_targetChanges != null)
            {
                Log("HitTarget", "{0} {1}", _targetChanges.Length, index);
                for (var i = 0; i < _targetChanges.Length; i++)
                {
                    Log("HitTarget", "{0} {1} {2}", _targetChanges.Length, _targetChanges[i].changes.Length, index);
                    if (_targetChanges[i].changes.Length <= index) break;
                    if (hit && index < _targetChanges[i].changes.Length)
                        _targetChanges[i].target.OnChange(_self, _targetChanges[i].changes[index]);
                    if (index == _targetChanges[i].changes.Length - 1)
                        _targetChanges[i].target.SignChange(_self, skill, -1);
                }
            }
        }

        protected void HitTarget(int index)
        {
            // if (index == 0) _self.OnHit(skill, time);
            if (_targetChanges != null)
            {
                Log("HitTarget", "{0} {1}", _targetChanges.Length, index);
                if (index >= _targetChanges.Length) return;
                for (var i = 0; i < _targetChanges[index].changes.Length; i++)
                    _targetChanges[index].target.OnChange(_self, _targetChanges[index].changes[i]);
                _targetChanges[index].target.SignChange(_self, skill, -1);
            }
        }

        protected void HitTargets()
        {
            // _self.OnHit(skill, time);
            if (_targetChanges != null)
            {
                Log("HitTargets", "{0}", _targetChanges.Length);
                for (var i = 0; i < _targetChanges.Length; i++)
                {
                    Log("HitTargets", "{0} {1}", _targetChanges.Length, _targetChanges[i].changes.Length);
                    for (var ii = 0; ii < _targetChanges[i].changes.Length; ii++)
                        _targetChanges[i].target.OnChange(_self, _targetChanges[i].changes[ii]);
                    _targetChanges[i].target.SignChange(_self, skill, -1);
                }
            }
        }

        protected void CastTarget(Entity target)
        {
            ObjectService.EntityManager.AddComponentData(entity, new CastTargetData { entity = target });
        }

        protected void CastTarget(Life target)
        {
            CastTarget(target.GetEntity());
        }

        protected EntityType CheckEntity(Entity entity)
        {
            var manager = ObjectService.EntityManager;
            var type = new EntityType();
            var _entity = _self.GetEntity();
            if (entity == _entity)
            {
                type.self = true;
            }
            else if (manager.HasComponent<EntityData>(entity))
            {
                var _entityData = manager.GetComponentData<EntityData>(_entity);
                var entityData = manager.GetComponentData<EntityData>(entity);
                type.partner = _entityData.IsPartner(entityData);
                type.enemy = _entityData.IsEnemy(entityData);
            }
            else if (manager.HasComponent<TerrainData>(entity))
            {
                type.terrain = true;
            }
            else if (manager.HasComponent<ShieldData>(entity))
            {
                type.shield = true;
                var _shieldEntity = manager.GetComponentData<ShieldData>(entity).master;
                if (_shieldEntity == _entity)
                {
                    type.self = true;
                }
                else if (manager.HasComponent<EntityData>(_shieldEntity))
                {
                    var _entityData = manager.GetComponentData<EntityData>(_entity);
                    var _shieldEntityData = manager.GetComponentData<EntityData>(_shieldEntity);
                    type.partner = _entityData.IsPartner(_shieldEntityData);
                    type.enemy = _entityData.IsEnemy(_shieldEntityData);
                }
            }

            return type;
        }

        /// <summary>
        ///     销毁
        /// </summary>
        protected void Destroy()
        {
            Log("Destroy");
            OnDestroyEffect();
            OnDestroy();
            DestroyEngine();
            _notice = null;
            _targets = null;
            _positions = null;
            _targetChanges = null;
        }

        protected void DestroyEngine()
        {
            ObjectService.EntityManager.DestroyEntity(entity);
            entity = Entity.Null;
            EngineService.single.DestroyEngine(gameObject);
        }

        protected void Log(string t)
        {
            if (_self.IsDebug())
                LogTools.Info("{0} id->{1} index->{2} {3}", _self.GetName(), _self.GetId(), _self.GetIndex(), t);
        }

        protected void Log(string t, string s, params object[] p)
        {
            var text = string.Format(s, p);
            if (_self.IsDebug())
                LogTools.Info("{0} id->{1} index->{2} {3} {4}", _self.GetName(), _self.GetId(), _self.GetIndex(), t,
                    text);
        }

        public void DebugLog(string t, string s, params object[] p)
        {
            var text = string.Format(s, p);
            LogTools.Error("{0} id->{1} index->{2} {3} {4}", _self.GetName(), _self.GetId(), _self.GetIndex(), t,
                    text);
        }

        public interface Notice
        {
            // 开始施放
            void Start(Skill skill);

            // 结束施放
            void End(Skill skill);

            // 准备施放
            void Ready(Skill skill);

            // 准备阶段 停止施放
            void Stop(Skill skill);

            // 施放阶段 取消施放
            void Cancel(Skill skill);
        }
    }
}